#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<ctype.h>

using namespace std;

const int baseize = 1024;
const int argvnum = 64;
const int envnum = 64;

char *gargv[argvnum];
int gargc =0;

int lastcode = 0;
char *genv[envnum];

char pwd[baseize];
char pwdenv[baseize];

#define NoneRedir 0
#define InputRedir 1
#define OutputRedir 2
#define AppRedir 3

int redir = NoneRedir;
char *filename = nullptr;

string GetUserName()
{
    string name = getenv("USER");
    return name.empty()?"None":name;
}

string GetHostName()
{
    string hostname = getenv("HOSTNAME");
    return hostname.empty()?"None":hostname;
}

string GetPwd()
{
    if(nullptr==getcwd(pwd,sizeof(pwd)))
    {
        return "None";
    }
    snprintf(pwdenv,sizeof(pwdenv),"PWD=%s",pwd);
    putenv(pwdenv);
    return pwd;
}

string LastDir()
{
    string curr=GetPwd();
    if(curr=="/"||curr=="None")
    {
       return curr;
    }
    size_t pos=curr.rfind("/");
    if(pos==std::string::npos)
    {
        return curr;
    }
    return curr.substr(pos+1);
}

string MakeCommandLine()
{
    char command_line[baseize];
    snprintf(command_line,baseize,"[%s@%s %s]",GetUserName().c_str(),GetHostName().c_str(),LastDir().c_str());
    return command_line;
}

//命令行提示符
void PrintfCommandLine()
{
    printf("%s",MakeCommandLine().c_str());
    fflush(stdout);
}

//获取用户的命令
bool GetCommandLine(char command_buffer[],int size)
{
     char *result = fgets(command_buffer,size,stdin);
    if(!result)
    {
        return false;

    }

    command_buffer[strlen(command_buffer)-1]=0;
    if(strlen(command_buffer)==0)
    {
        return true;
    }
}

void ResetCommandLine()
{
    memset(gargv,0,sizeof(gargv));
    gargc=0;

    redir = NoneRedir;
    filename = nullptr;

}

void ParseRedir(char command_buffer[],int len)
{
    int end = len-1;
    while (end>=0)
    {
        if(command_buffer[end]=='<')
        {
            redir=InputRedir;
            command_buffer[end]=0;
            filename=&command_buffer[end]+1;
            TrimSpace(filename);
            break;
        }
        else if(command_buffer[end]=='>')
        {
            if(command_buffer[end-1]=='>')
            {
                redir=AppRedir;
                command_buffer[end]=0;
                command_buffer[end-1]=0;
                filename=&command_buffer[end]+1;
                TrimSpace(filename);
                break;
            }
            else
            {
            redir=OutputRedir;
            command_buffer[end]=0;
            filename=&command_buffer[end]+1;
            TrimSpace(filename);
            break;
            }
        }
        else
        {
            end--;
        }
    }
}

void ParseCommand(char command_buffer[])
{
    const char *sep=" ";
    gargv[gargc++]=strtok(command_buffer,sep);
    while((bool)(gargv)[gargc]=strtok(nullptr,sep));
    gargc--;

}

void ParseCommandLine(char command_buffer[],int len)
{
    ResetCommandLine();
    ParseRedir(command_buffer,len);
    ParseCommand(command_buffer);

}

void debug()
{
    printf("argc:%d\n",gargc);
    for(int i=0;gargv[i];i++)
    {
        printf("argv[%d]:%s\n",i,gargv[i]);
    }
}

void DoRedir()
{
    if(redir==InputRedir)
    {
        if(filename)
        {
            int fd = open(filename,O_RDONLY);
            if(fd<0)
            {
                exit(2);
            }
        }
        else
        {
            exit(1);
        }
    }
    else if(redir==OutputRedir)
    {
        if(filename)
        {
            int fd=open(filename,O_CREAT|O_WRONLY|O_TRUNC,0666);
            if(fd<0)
            {
                exit(4);
            }
            dup(fd,1);
        }
        else
        {
            exit(3);
        }
    }
    else if(redir==AppRedir)
    {
        if(filename)
        {
            int fd=open(filename,O_CREAT|O_WRONLY|O_APPEND,0666);
            if(fd<0)
            {
                exit(6);
            }
            dup2(fd,1);
        }
        else
        {
            exit(5);
        }
    }
    else
    {

    }
}

bool ExecuteCommand()
{
    pid_t id = fork();
    if(id<0)
    {
        return false;
    }
    if(id==0)
    {
        //子进程
        DoRedir();
        //执行命令
        execvpe(gargv[0],gargv,genv);
        //退出
        exit(7);
    }
    int status=0;
    pid_t rid=waitpid(id,&status,0);
    if(rid>0)
    {
        if(WIFEXITED(status))
        {
            lastcode=WEITSTATUS(status);
        }
        else
        {
            lastcode=100;
        }
        return true;
    }
    return false;
}

void AddEnv(const char *item)
{
    int index=0;
    while(genv[index])
    {
        index++;
    }

    genv[index]=(char*)malloc(strlen(item)+1);
    strncpy(genv[index],item,strlen(item+1));
    genv[++index]=nullptr;
}

bool CheckAndExecCommand()
{
    if(strcmp(gargv[0],"cd")==0)
    {
         if(gargc==2)
        {
            chdir(gargv[1]);
            lastcode=0;
        }
        else
        {
            lastcode=1;
        }
        return true;
    }
    else if(strcmp(gargv[0],"export")==0)
    {
        if(gargc==2)
        {
            AddEnv(gargv[1]);
            lastcode=0;
        }
        else
        {
            lastcode=2;
        }
        return true;
    }
    else if((strcmp(gargv[0]),"env")==0)
    {
        for(int i=0;genv[i];i++)
        {
            printf("%s\n",genv[i]);
        }
        lastcode=0;
        return true;
    }
    else if (strcmp(gargv[0],"echo")==0)
    {
        if(gargc==2)
        {
            if(gargv[1][0]=='$')
            {
                if (gargv[1][1]=='?')
                {
                    printf("%d\n",lastcode);
                    lastcode=0;
                }
            }
            else
            {
                printf("%s\n",gargv[1]);
                lastcode=0;
            }
        }
        else
        {
            lastcode=3;
        }
        return true;
    }
    return false;
}

void InitEnv()
{
    extern char **environ;
    int index=0;
    while(environ[index])
    {
        genv[index]=(char*)malloc(strlen(environ[index]+1));
        strncpy(genv[index],environ[index],strlen(environ[index])+1);
        index++;
    }
    genv[index]=nullptr;
}

int main()
{
    InitEnv();
    char command_buffer[baseize];
    while (true)
    {
        PrintfCommandLine();
        if(!GetCommandLine(command_buffer,baseize))
        {
            continue;
        }
        ParseCommandLine(command_buffer,strlen(command_buffer));

        if(CheckAndExecCommand())
        {
            continue;
        }
        ExecuteCommand();
    }
    return 0;
}
